package com.zang.netty.io.nio;

import com.zang.netty.io.model.enums.SocketEnum;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author Zhang Qiang
 * @Date 2019/7/19 10:15
 */
public class TimeClientHandleByNio implements Runnable{
    private String host;
    private int port;
    private Selector selector;
    private SocketChannel socketChannel;
    private volatile boolean stop;

    public TimeClientHandleByNio(String host, int port){
        this.host = host == null ? SocketEnum.LOCALHOST.getStr() : host;
        this.port = port == 0 ? SocketEnum.NIO_PORT.getNum() : port;
        try{
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            // 设置异步非阻塞
            socketChannel.configureBlocking(false);
        } catch (IOException e) {
            e.printStackTrace();
            //按照惯例，非零状态码表示异常终止
            System.exit(1);
        }

    }

    public void run() {
         /**
          * 1.执行连接
          */
        try {

            doConnect();

        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

         /**
          * 2.执行注册与读取、发送
          */
        while (!stop){
            try {
                selector.select(800);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey selectionKey = null;

                while (it.hasNext()){
                    selectionKey = it.next();
                    it.remove();
                    try {
                        handleInput(selectionKey);
                    }catch (Exception e){
                        if (null != selectionKey){
                            selectionKey.cancel();
                            if (selectionKey.channel() != null){
                                selectionKey.channel().close();
                            }
                        }
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }

        if ( selector != null){
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }



    }

    private void handleInput(SelectionKey key){

        if (key.isValid()){
            System.out.println("连接已成功");
            SocketChannel sc = (SocketChannel) key.channel();

            //测试此密钥的通道是否已完成其套接字连接操作。
            if (key.isConnectable()){
                System.out.println("key.isConnectable() = true");
                try {
                    //确认连接是否建立
                    if (sc.finishConnect()){
                        System.out.println("连接确认已经建立");
                        sc.register(selector, SelectionKey.OP_READ);
                        doWrite(sc);
                    }else {
                        System.out.println("连接失败，进程退出");
                        System.exit(1);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (key.isReadable()){
                try {
                    ByteBuffer readBuff = ByteBuffer.allocate(1024);
                    // 读取缓冲区内容到readBuff里，返回值代表读取了多少内容
                    int readByte = sc.read(readBuff);
                    if (readByte > 0){
                        //切换模式
                        readBuff.flip();
                        byte[] bytes = new byte[readBuff.remaining()];
                        readBuff.get(bytes);
                        String body = new String(bytes, "UTF-8");
                        System.out.println("now is : " + body);
                        this.stop = true;
                    }else if (readByte < 0 ){
                        key.cancel();
                        sc.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

     /**
      * 通道执行连接 并设置状态为读
      */
    private void doConnect() throws IOException {
        System.out.println("开始连接： " + host + ":" + port);
        // 如果直接连接成功则注册到多路复用器上发送请求消息，然后读取应答消息
        // 若此通道处于非阻塞模式，则此方法的调用将启动非阻塞连接操作
        // 如果直接连接成功则将SocketChannel注册到选择器上，设置 SelectionKey.OP_READ
        // 如果直连不成功说明服务端还没有返回TCP握手应答消息，不代表连接失败，这时候需要将SocketChannel注册的时候设置为OP_CONNECT，这样服务端返回TCP消息以后选择器即可轮询到这个通道处于连接就绪状态
        if (socketChannel.connect(new InetSocketAddress(host, port))){

            System.out.println("register,SelectionKey.OP_READ");
            socketChannel.register(selector, SelectionKey.OP_READ);
            System.out.println("执行doWrite()");
            doWrite(socketChannel);
        }else {
            System.out.println("register,SelectionKey.OP_CONNECT");
             /**
              * SelectionKey.OP_ACCEPT —— 接收连接继续事件，表示服务器监听到了客户连接，服务器可以接收这个连接了
              * SelectionKey.OP_CONNECT —— 连接就绪事件，表示客户与服务器的连接已经建立成功
              * SelectionKey.OP_READ —— 读就绪事件，表示通道中已经有了可读的数据，可以执行读操作了（通道目前有数据，可以进行读操作了）
              * SelectionKey.OP_WRITE —— 写就绪事件，表示已经可以向通道写数据了（通道目前可以用于写操作）
              */
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
        }
    }

     /**
      * 发送
      */
    private void doWrite(SocketChannel sc) throws IOException {
        System.out.println("开始发送，请输入发送信息：");
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String str = bf.readLine();
        str = "QUERY TIME ORDER".equals(str) || "please".equals(str) ? "QUERY TIME ORDER" : str;
        byte[] req = str.getBytes();
        ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
        writeBuffer.put(req);
        writeBuffer.flip();
        sc.write(writeBuffer);
        // 是否还存在元素
        if (!writeBuffer.hasRemaining()){
            System.out.println(" Send order " + " server succeed ");
        }

    }


}

















